home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / lisp / clue.lha / clue / clos-kludge / clos.l < prev    next >
Text File  |  1989-07-12  |  28KB  |  729 lines

  1. ;;; -*- Mode:Common-Lisp; Package:CLUEI; Syntax:COMMON-LISP; Base:10; Lowercase:T -*-
  2.  
  3. ;;;
  4. ;;;             TEXAS INSTRUMENTS INCORPORATED
  5. ;;;                  P.O. BOX 2909
  6. ;;;                   AUSTIN, TEXAS 78769
  7. ;;;
  8. ;;; Copyright (C) 1988 Texas Instruments Incorporated.
  9. ;;;
  10. ;;; Permission is granted to any individual or institution to use, copy, modify,
  11. ;;; and distribute this software, provided that this complete copyright and
  12. ;;; permission notice is maintained, intact, in all copies and supporting
  13. ;;; documentation.
  14. ;;;
  15. ;;; Texas Instruments Incorporated provides this software "as is" without
  16. ;;; express or implied warranty.
  17. ;;;
  18.  
  19. ;;; Hacked subset of CLOS that implements the a minimal object programming environment based on defstructs.
  20. ;;; The following are impemented:
  21. ;;;
  22. ;;; DEFCLASS
  23. ;;;   At most one super-class is supported (no multiple inheritance)
  24. ;;;   The only slot options supported are :INITARG :INITFORM, :TYPE, :READER and :ACCESSOR
  25. ;;;   :ALLOCATION is always :instance even when specified :class or :dynamic
  26. ;;;   The only class options supported are :DOCUMENTATION
  27. ;;;
  28. ;;; DEFMETHOD
  29. ;;;  Method qualifiers are not allowed
  30. ;;;   only one parameter-specializer is supported
  31. ;;;   :around method qualifiers are NOT supported
  32. ;;;
  33. ;;; CALL-NEXT-METHOD
  34. ;;;   The only method combination supported is :most-specific-first.
  35. ;;;
  36. ;;; WITH-SLOTS
  37. ;;;   Does direct substitution, not a code walk.
  38. ;;;
  39. ;;; SLOT-VALUE
  40. ;;;   A macro that only works when the first parameter is typed
  41. ;;;   with (the CLASS value), and the slot-name must be a constant.
  42. ;;;
  43. ;;; MAKE-INSTANCE
  44. ;;; CLASS-OF
  45. ;;; CMAKUNBOUND
  46. ;;; CBOUNDP
  47. ;;; SLOT-EXISTS-P
  48. ;;;
  49. ;;; KNOWN PROBLEMS:
  50. ;;; ---------------
  51. ;;; This system introduces load-order dependencies between files 
  52. ;;; that have the same method name.  For example:
  53. ;;; The system consists of files A B and C, compiled and loaded in that order,
  54. ;;; files B and C containing defmethod XXX.  If file A is modified to contain
  55. ;;; a method XXX (for a different class) it will have a new generic method for
  56. ;;; the XXX method.  However, files B and C haven't changed and so don't get
  57. ;;; re-compiled. When reloading the files, A will load the correct generic method,
  58. ;;; but it gets clobered by the old (incorrect) generic methods in files B and C.
  59. ;;; The moral of the story is: Recompile EVERYTHING often.
  60. ;;;
  61. ;;; Change history:
  62. ;;;
  63. ;;;  Date    Author    Description
  64. ;;; -------------------------------------------------------------------------------------
  65. ;;; 08/12/87    LGO    Created
  66. ;;; 11/23/87    LGO    Added :before and :after method qualifiers
  67. ;;; 01/11/88    LGO    Changed copy-list to copy-tree in insert-method
  68. ;;;            This fixes the nasty disappearing :after method bug.
  69. ;;; 02/02/88    LGO    Added &rest arg to call-next-method
  70. ;;;            Removed :accessor-prefix option from DEFCLASS
  71. ;;;            Added SLOT-VALUE, CMAKUNBOUND and CBOUNDP.
  72. ;;;            Changed the definition of WITH-SLOTS to conform to
  73. ;;;            the new CLOS spec.
  74. ;;; 02/12/88    LGO    Added UNDEFMETHOD
  75. ;;; 02/12/88    LGO    Added PRINT-INSTANCE method
  76. ;;; 02/18/88    LGO    Added ALLOCATOR slot to standard-class
  77. ;;; 02/18/88    LGO    Added class si:type-predicate property to speed-up TYPEP
  78. ;;; 02/26/88    LGO    Added compile-time type checking of initforms to defclass.
  79. ;;; 03/22/88    LGO    Added slot-exists-p
  80. ;;; 03/23/88    LGO    Initialize-instance now initializes inherited slots
  81. ;;; 03/23/88    LGO    Don't put initializations (or types) in defstruct
  82. ;;;            [This speeds up make-instance greatly]
  83. ;;; 03/24/88    LGO    Use CASE instead of TYPECASE for initialize-instance
  84. ;;; 08/10/88    LGO    Replace symbol-class with find-class
  85. ;;; 08/24/88    LGO    Use correct arglist ordering for setf methods
  86. ;;; 08/25/88    LGO    Use subclass-p instead of subtypep in insert-method
  87. ;;;            (works around a bug in Franz CL)
  88. ;;; 10/04/88    LGO    Add fake slot-boundp
  89.  
  90. (in-package 'cluei :use '(lisp xlib))
  91.  
  92. (pushnew :CLOS-KLUDGE *features*)
  93.  
  94. (export '(defclass
  95.        defmethod
  96.        call-next-method
  97.        with-slots
  98.        slot-value
  99.        make-instance
  100.        class-of
  101.        find-class
  102.        cmakunbound
  103.        cboundp
  104.        slot-exists-p
  105.        slot-boundp
  106.        initialize-instance
  107.  
  108.        undefmethod
  109.        ))
  110.  
  111. ;;; if FORM is defined as the list
  112. ;;;      '((X Y Z) 
  113. ;;;       "this is a test doc-string"
  114. ;;;       (DECLARE (ARGLIST (X Y Z)) 
  115. ;;;                  (VALUES (+ X Y Z)) (SPECIAL icky pick wicky) (inline mumble) (income tax))
  116. ;;;       (+ x y z)))
  117. ;;; then (PARSE-BODY (CDR FORM) nil t) would return three values:
  118. ;;;  1) ((+ X Y Z))
  119. ;;;  2) ((DECLARE (ARGLIST (X Y Z)) 
  120. ;;;                 (VALUES (+ X Y Z)) (SPECIAL ICKY PICK WICKY) (INLINE MUMBLE) (INCOME TAX)))
  121. ;;;  3) "this is a test doc-string"
  122. ;;; and (PROCESS-DEFUN-BODY 'foo form nil t) would return the list
  123. ;;;  (NAMED-LAMBDA 
  124. ;;;      (FOO (:DESCRIPTIVE-ARGLIST (X Y Z)) (:VALUES (+ X Y Z))) (X Y Z) 
  125. ;;;      "this is a test doc-string" 
  126. ;;;      (DECLARE (SPECIAL ICKY PICK WICKY) (INLINE MUMBLE) (INCOME TAX)) 
  127. ;;;      (BLOCK FOO (+ X Y Z)))
  128.  
  129. #-ti
  130. (defun parse-body (body environment &optional (doc-string-allowed t))
  131.   "[From SPICE]:This function is to parse the declarations and doc-string out
  132.   of the body of a defun-like form.  Body is the list to be parsed and consists
  133.   of everything after the formal parameter list.
  134.   Environment is the lexical environment to expand macros in.  If
  135.   Doc-String-Allowed is true, then a doc string will be parsed out of the body
  136.   and returned.  If it is false then a string will terminate the search for
  137.   declarations.  Three values are returned: the tail of Body after the
  138.   declarations and doc strings, a list of declare forms, and the doc-string,
  139.   or NIL if none."
  140.   (declare (values body decs doc-string))
  141.   (let (decls
  142.     doc)
  143.     (do ((tail body (cdr tail)))
  144.     ((endp tail)
  145.      (values tail 
  146.          (nreverse (the list decls)) 
  147.          doc))
  148.       (let ((form (car tail)))
  149.     (cond ((and (stringp form) (cdr tail))
  150.            (if doc-string-allowed
  151.            (setq doc form)
  152.            (return (values tail 
  153.                    (nreverse (the list decls))
  154.                    doc))))
  155.           ((not (and (consp form) (symbolp (car form))))
  156.            (return (values tail
  157.                    (nreverse (the list decls))
  158.                    doc)))
  159.           ((eq (car form) 'declare)
  160.            (push form decls))
  161.           (t
  162.            (multiple-value-bind (res win)
  163.            (macroexpand form environment)
  164.          (if (and win (consp res) (eq (car res) 'declare))
  165.              (push res decls)
  166.              (return (values tail
  167.                      (nreverse (the list decls))
  168.                      doc))))))))))
  169.  
  170. (defun macro-warn (string &rest format-args)
  171.   #+explorer
  172.   (apply #'sys:record-and-print-warning nil nil nil string format-args)
  173.   #-explorer
  174.   (apply #'warn string format-args))
  175.  
  176. (defstruct (standard-class (:constructor internal-initialize-standard-class)
  177.                (:copier nil))
  178.   (name nil :type symbol)            ; Class name
  179.   (superclass-names nil :type list)        ; List of parent class names
  180.   (slots nil :type list)            ; list of slot names for this class and superclasses
  181.                         ;; Slots is (mapcar #'variable-name variables)
  182.   (variables nil :type list)            ; list of variable for this class and superclasses
  183.   (prefix "" :type string)            ; Accessor prefix string
  184.   (allocator nil)                ; Allocator function
  185.   ;; class options:
  186.   (default-initargs nil :type list)
  187.   (documentation "" :type string)
  188.   (metaclass 'standard-class :type symbol)
  189.   )
  190.  
  191. (defstruct (variable (:type vector) (:copier nil))
  192.   name initarg initform)
  193.  
  194. (proclaim '(inline class-name))
  195. (defun class-name (class) (standard-class-name class))
  196.  
  197. (defconstant *class-property* 'meta-class)
  198.  
  199. (defun find-class (class-name &optional (errorp t) environment)
  200.   (declare (inline find-class)
  201.        (ignore environment))  
  202.   (or (get class-name *class-property*)
  203.       (and errorp (error "~s isn't a class-name" class-name))))
  204.  
  205. (defsetf find-class (class-name &optional errorp environment) (new-name)
  206.   (declare (ignore errorp environment))
  207.   `(setf (get ,class-name *class-property*) ,new-name))
  208.  
  209. (defun cboundp  (class &optional environment)
  210.   "Returns T if CLASS is the name of a class in ENVIRONMENT"
  211.   (declare (type symbol class)
  212.        (ignore environment))
  213.   (and (get class *class-property*) t))
  214.  
  215. (defun cmakunbound (class-name &optional environment)
  216.   ;; Cause the CLASS symbol to no-longer name a class in ENVIRONMENT
  217.   (declare (type symbol class-name)
  218.        (ignore environment))
  219.   (let ((class (get class-name *class-property*)))
  220.     (when class
  221.       (dolist (super (standard-class-superclass-names class))
  222.     (cmakunbound super))
  223.       (remprop class-name *class-property*))))
  224.  
  225. #-explorer
  226. (defun class-of (object) (get (type-of object) *class-property*))
  227. #+explorer
  228. (defun class-of (object) (get (si:array-leader object 1) *class-property*))
  229.       
  230. #+explorer
  231. (compiler::add-optimizer class-name class-name-opt)
  232. #+explorer
  233. (defun class-name-opt (form)
  234.   ;; Optimize (class-name (class-of thing)) to (si:array-leader thing 1)
  235.   ;; This is used by CLASS-NAME-OF, which needs to be fast.
  236.   (if (and (consp (cadr form))
  237.        (eq (caadr form) 'class-of))
  238.       `(si:array-leader ,@(cdadr form) 1)
  239.     form))
  240.  
  241. (defun slot-exists-p (object slot-name)
  242.   "Returns T when SLOT-NAME is defined for OBJECT."
  243.   (let* ((class (class-of object)))
  244.     (and (member slot-name (standard-class-slots class) :test #'eq) t)))
  245.  
  246. (proclaim '(inline subclass-p))
  247. #+comment
  248. (defun subclass-p (subclass class)
  249.   ;; Returns T when SUBCLASS is a subclass of CLASS
  250.   (if (and (symbolp subclass) (symbolp class))
  251.       (let* ((pclass (get subclass *class-property*))
  252.          (parents (and pclass (standard-class-superclass-names pclass))))
  253.     (or (member class parents :test #'eq)
  254.         (and parents
  255.          (dolist (p parents)
  256.            (and (subclass-p p class)
  257.             (return t))))))
  258.     (subtypep subclass class)))
  259.  
  260. ;; This version assumes only one parent per class
  261. (defun subclass-p (subclass class)
  262.   ;; Returns T when SUBCLASS is a subclass of CLaSS, and is twice as fast
  263.   (if (and (symbolp subclass) (symbolp class))
  264.       (or (eq class t) ;; everything's a subclass of T
  265.       (do ((pclass (get subclass *class-property*)))
  266.           ((null pclass) nil)
  267.         (let ((parent (car (standard-class-superclass-names pclass))))
  268.           (when (eq class parent)
  269.         (return t))
  270.           (setq pclass (get parent *class-property*)))))
  271.     (subtypep subclass class)))
  272.  
  273. (defmacro defclass (class-name superclass-names slots &rest options)
  274.   (let* ((documentation nil)
  275.      (prefix (concatenate 'string "%INTERNAL-" (string class-name) "-"))
  276.      (internal-initializer (intern (concatenate 'string "INTERNAL-INITIALIZE-" (string class-name))))
  277.      (meta-class (or (second (assoc :metaclass options)) 'standard-class))
  278.      (readers nil)
  279.      (accessors nil)
  280.      (variables nil)
  281.      (class-allocated nil)
  282.      (slot-names (mapcar #'(lambda (slot) (if (atom slot) slot (car slot))) slots))
  283.      (superclasses (mapcar #'find-class superclass-names))
  284.      (inherited-variables (mapcan #'(lambda (v) (copy-list (standard-class-variables v))) superclasses))
  285.      (inherited-slots (mapcan #'(lambda (s) (copy-list (standard-class-slots s))) superclasses))
  286.      new-slots);
  287.     ;; The following is equivalent to:
  288.     ;; (set-difference slot-names inherited-slots)))
  289.     ;; except slot ordering is preserved.
  290.     (setq new-slots (remove-if #'(lambda (x) (member x inherited-slots :test #'eq))
  291.                    (The List slot-names)))
  292.     (setq slots                    ; Reformat slots for defstruct
  293.       (mapcan
  294.         #'(lambda (slot)
  295.         (if (atom slot) (list slot)
  296.           (apply
  297.             #'(lambda (name &key initarg (initform nil initform-p) type
  298.                     accessor reader (allocation :instance))
  299.             (declare (ignore allocation)) ;; always use :instance
  300.             (when (or initarg initform-p)
  301.               (setq inherited-variables
  302.                 (delete name inherited-variables :key #'variable-name))
  303.               (push (make-variable :name name :initarg initarg :initform initform)
  304.                 variables))
  305.             (when (or reader accessor)
  306.               (push (list name (or reader accessor)) readers))
  307.             (when accessor
  308.               (push (list name accessor) accessors))
  309.             ;; Check initform type
  310.             #+comment
  311.             (when (and initform-p type (constantp initform))
  312.               (si:ignore-errors ;; TYPEP may error if type isn't defined yet
  313.                 (unless (typep (eval initform) type)
  314.                   (macro-warn "Warning: For class ~s the initform for slot ~s, ~s isn't ~s"
  315.                       class-name name initform type))))
  316.             ;; build defstruct slot descriptor
  317.             `((,name ,initform ,@(and type `(:type ,type)))))
  318.              slot)))
  319.           slots))
  320.     (setq variables (nconc (reverse variables) inherited-variables))
  321.     (when (cdr superclass-names)
  322.       (error "Multiple superclass-names not supported"))
  323.     (setq documentation (second (assoc :documentation options)))
  324.     `(progn
  325.        (eval-when (compile load eval)
  326.      (setf (get ',class-name *class-property*)
  327.            (make-instance ',meta-class
  328.          :name ',class-name
  329.          :prefix ,prefix
  330.          :allocator ',internal-initializer
  331.          :superclass-names ',superclass-names
  332.          :slots ',(union slot-names inherited-slots)
  333.          :variables ',variables
  334.          ;; Quote the options
  335.          ,@(do ((option options (cdr option))
  336.             (result nil))
  337.                ((endp option) result)
  338.              (setq result (nconc `(,(caar option) ',(cdar option)) result))))))
  339.        (defstruct (,class-name
  340.            (:conc-name ,(intern prefix))
  341.            (:constructor ,internal-initializer ()) ;; Nil arglist for faster construction
  342.            #-symbolics ;; Symbolics REQUIRES a predicate for typep to work.
  343.            (:predicate nil)
  344.            (:copier nil)
  345.            (:print-function print-instance)
  346.            ,@(when superclass-names `((:include ,@superclass-names))))
  347.      ,@(when documentation (list documentation))
  348.      ,@new-slots)
  349.  
  350.        ,@(mapcan #'(lambda (slot)
  351.              (let* ((name (car slot))
  352.                 (type (getf (cdr slot) :type))
  353.                 (initform (getf (cdr slot) :initform))
  354.                 (accessor (intern (concatenate 'string prefix (string (car slot))))))
  355.                `((defmacro ,accessor (instance)
  356.                (list 'progn instance
  357.                  ',(if type
  358.                        `(the ,type (get ',class-name ',name))
  359.                      `(get ',class-name ',name))))
  360.              ,@(when initform
  361.                  `((setf (get ',class-name ',name) ,initform))))))
  362.          class-allocated)
  363.                         ; Define a default initialize-instance method
  364.        (defmethod initialize-instance ((instance ,class-name) &rest options)
  365.      options ;; may not be used
  366.      ,@(mapcar
  367.          #'(lambda (variable)
  368.          (let ((slot (variable-name variable))
  369.                (initarg (variable-initarg variable))
  370.                (initform (variable-initform variable)))
  371.            (if initform
  372.                `(setf (slot-value (the ,class-name instance) ',slot)
  373.                   ,(if initarg
  374.                    `(or (getf options ',initarg) ,initform)
  375.                  initform))
  376.              (when initarg
  377.                `(let ((value (getf options ',initarg)))
  378.               (when value
  379.                 (setf (slot-value (the ,class-name instance) ',slot) value)))))))
  380.          variables)
  381.      instance)
  382.        ,@(mapcar #'(lambda (reader)        ;Define reader methods
  383.              `(defmethod ,(second reader) ((self ,class-name))
  384.             (,(intern (concatenate 'string prefix
  385.                            (string (first reader)))) self)))
  386.          readers)
  387.        
  388.        ,@(mapcar #'(lambda (reader)        ;Define setf methods
  389.              `(defmethod (setf ,(second reader)) (value (self ,class-name))
  390.             (setf (,(intern (concatenate 'string prefix
  391.                              (string (first reader)))) self) value)))
  392.          accessors)
  393.        ',class-name
  394.        )))
  395.  
  396. (defun print-instance (instance stream depth)
  397.   (declare (ignore depth))
  398.   (print-object instance stream))
  399.  
  400. (defmacro inhibit-fdefine-warnings (&body body)
  401.   #+ti
  402.   `(let ((si:inhibit-fdefine-warnings t)) ,@body)
  403.   #+symbolics  ;; Symbolics can't wrap a defun inside a let
  404.   `(progn (setq *save-inhibit-fdefine-warnings* si:inhibit-fdefine-warnings
  405.         si:inhibit-fdefine-warnings t)
  406.       ,@body
  407.       (setq si:inhibit-fdefine-warnings *save-inhibit-fdefine-warnings*))
  408.   #-(or ti symbolics)
  409.   `(progn ,@body)
  410.   )
  411. ;; Allow forms within inhibit-fdefine-warnings to start in column 0
  412. #+ti (setf (get 'inhibit-fdefine-warnings 'si:may-surround-defun) t)
  413.  
  414. (defmacro defmethod (name lambda-list &body body &environment env)
  415.   (if (consp name)
  416.       (progn
  417.     (unless (eq (car name) 'setf)
  418.       (error "Invalid method name ~s" name))
  419.     (let ((setfer (intern (concatenate 'string "SET-" (string (second name))))))
  420.       `(progn ;; The first parameter to a setf-method is the value.  Reverse that here.
  421.          (defmethod ,setfer (,@(last lambda-list) ,@(butlast lambda-list)) ,@body)
  422.          (eval-when (compile load eval)
  423.            (inhibit-fdefine-warnings
  424.          (defsetf ,(second name) ,setfer))))))
  425.     (let (type n args code decs doc
  426.       (qualifier nil)
  427.       $method $type $lambda-list)
  428.       (declare (special $method $type $lambda-list))
  429.       (when (keywordp lambda-list)
  430.     (setq qualifier lambda-list
  431.           lambda-list (pop body)))
  432.       (multiple-value-setq (code decs doc)
  433.     (parse-body body env))
  434.       ;; Find the (first) specialized parameter in the lambda list,
  435.       (do ((arguments lambda-list (cdr arguments))
  436.        (arg)
  437.        (i 0 (1+ i)))
  438.       ((or (endp arguments)
  439.            (member (setq arg (car arguments)) lambda-list-keywords))
  440.        (error "Method has no type"))
  441.     (when (consp arg)
  442.       ;; Save the type and position of the specialized parameter
  443.       (setq type (second arg)
  444.         n i
  445.         args (copy-list lambda-list))
  446.       ;; Special case for EQL types
  447.       (when (and (consp type) (eq (car type) 'eql))
  448.         (unless (constantp (cadr type))
  449.           (error "Non-constant parameter specializers not supported"))
  450.         (setq type (list 'member (eval (cadr type)))))
  451.       ;; Form the method arglist
  452.       (setf (nth i args) (first arg))
  453.       (return nil)))
  454.       ;; Generate code for method and generic method
  455.       (let* ((code-name (intern (format nil "~@[~a-~]~a-~a-METHOD" qualifier name type)
  456.                 (symbol-package name)))
  457.          (methods (insert-method name type code-name qualifier)))
  458.     `(progn
  459.        (eval-when (compile load eval) (setf (get ',name 'methods) ',methods))
  460.        ;; Define the method
  461.        (compiler-let (($method ',name)
  462.               ($type ',type)
  463.               ($lambda-list ',args))
  464.          ,(if (cdr methods) ;; If more than one (us)
  465.           `(defun ,code-name ,args ,@decs ,(nth n args) ,@code)
  466.         `(defun ,name ,args ,@(and doc (list doc)) ,@decs ,(nth n args) ,@code)))
  467.        ;; Define the generic method
  468.        ,(if (cdr methods) ;; If more than one (us)
  469.         `(inhibit-fdefine-warnings
  470.            ,(make-generic-function name n methods :documentation doc))
  471.           ;; Speed hack for when there's only one method
  472.           `(setf (symbol-function ',code-name) (symbol-function ',name)))
  473.        ',name)))))
  474.  
  475. (defun make-generic-function (name n methods &key documentation &aux no-otherwise)
  476.   `(defun ,name (&rest args)
  477.      ,@(and documentation (list documentation))
  478.      (declare (type list args))
  479.      (let ((discriminator (nth ,n args)))
  480.        ;; It may be faster to do (case (type-of discriminator) ...)
  481.        (,@(if (eq name 'initialize-instance)
  482.           ;; Speed hack for the methods that EVERY class has.
  483.           '(case #+explorer (si:array-leader discriminator 1)
  484.              #-explorer (type-of discriminator))
  485.         '(typecase discriminator))
  486.     ,@(do ((method methods (cdr method))
  487.            (result nil))
  488.           ((endp method) (nreverse result))
  489.         (let* ((class (caar method))
  490.            (main (getf (cdar method) nil))
  491.            (before (find-methods class :before method))
  492.            (after (find-methods class :after method)))
  493.           (when (or main before after)
  494.         (unless main (setq main (first (find-methods class nil method))))
  495.         (when (eq class 't) (setq no-otherwise t))
  496.         (push `(,class
  497.             ,@(mapcar #'(lambda (method) `(apply (function ,method) args))
  498.                   before)
  499.             (multiple-value-prog1
  500.               ,(if main `(apply (function ,main) args)
  501.                  `(error "Method ~s has no primary handler for ~s" ',name discriminator))
  502.               ,@(mapcar #'(lambda (method) `(apply (function ,method) args))
  503.                     (nreverse after))))
  504.               result))))
  505.     ,@(unless no-otherwise
  506.         `((otherwise (error "Method ~s has no handler for ~s" ',name discriminator))))))))
  507.  
  508. (defun find-methods (class qualifier methods)
  509.   (do ((method methods (cdr method))
  510.        (result nil))
  511.       ((endp method) (nreverse result))
  512.     (when (or (eq class (caar method))
  513.           (subclass-p class (caar method)))
  514.       (let ((name (getf (cdar method) qualifier)))
  515.     (when name (push name result))))))
  516.  
  517. (defun insert-method (name class method qualifier)
  518.   ;; The METHODS property of the method name contains an alist of the form:
  519.   ;; (class qualifier method-name qualifier method-name ...)
  520.   ;; The methods list is sorted by subtype, most specific first.
  521.   (unless (member qualifier '(nil :before :after))
  522.     (error "Method qualifier ~s not handled" qualifier))
  523.   (let* ((list (copy-tree (get name 'methods)))
  524.      (old (assoc class list :test #'equal)))
  525.     (if old
  526.     (setf (getf (cdr old) qualifier) method)
  527.       (do* ((new (list class qualifier method))
  528.         (previous nil entry)
  529.         (entry list (cdr entry)))
  530.        ((or (endp entry)
  531.         (subclass-p class (caar entry)))
  532.         (if previous
  533.         (setf (cdr previous)
  534.               (cons new entry))
  535.           (setq list (cons new list))))))
  536.     list))
  537.  
  538. (defun delete-method (name class method qualifier)
  539.   ;; The METHODS property of the method name contains an alist of the form:
  540.   ;; (class qualifier method-name qualifier method-name ...)
  541.   (unless (member qualifier '(nil :before :after))
  542.     (error "Method qualifier ~s not handled" qualifier))
  543.   (let* ((list (copy-tree (get name 'methods)))
  544.      (old (assoc class list)))
  545.     (if old
  546.     (progn
  547.       (remf (cdr old) qualifier)
  548.       (unless (cdr old)
  549.         (setq list (delete class list :key #'car))))
  550.       (if (fboundp method)
  551.       (error "Method entry not found for ~s" method)
  552.     (macro-warn "Warning: Method ~s ~:[~;~s ~]for class ~s not defined." name qualifier class)))
  553.     list))
  554.  
  555. ;; CLOS requires this to be a function
  556. (defmacro call-next-method (&rest args)
  557.   "Run the next method up in the type heiarchy.  Use ONLY within a DEFMETHOD.
  558.  Returns the first value of the last method found."
  559.   (declare (special $method $type $lambda-list))
  560.   (unless (boundp '$method) (error "Call-Next-Method executed outside of a defmethod"))
  561.   (let ((rest (member '&rest $lambda-list) ))
  562.     (cond (args `(call-next-method-internal ',$type ',$method ,@args))
  563.       (rest                    ;Rest arg hair
  564.        `(apply #'call-next-method-internal ',$type ',$method
  565.            ,@(subseq $lambda-list 0 (- (length $lambda-list) (length rest))) ,(second rest)))
  566.       ((setq rest (member '&key $lambda-list))    ;Keyword arg hair
  567.        `(call-next-method-internal ',$type ',$method
  568.                        ,@(subseq $lambda-list 0 (- (length $lambda-list) (length rest)))
  569.                        ,@(do* ((vars (cdr rest) (cdr vars))
  570.                            (var (car vars) (car vars))
  571.                            (result nil))
  572.                          ((or (endp vars) (member var lambda-list-keywords))
  573.                           (nreverse result))
  574.                        (push (intern (string var) 'keyword) result)
  575.                        (push var result))))
  576.       (t `(call-next-method-internal ',$type ',$method ,@$lambda-list)))))
  577.  
  578. (defun call-next-method-internal (type method &rest args)
  579.   ;; Internal function to call METHOD of one of TYPE's parent types.
  580.   ;; Returns the first value of the last method found.
  581.  
  582.   ;; The following DO is an in-line expansion of (member type (get method 'methods) :key #'car)
  583.   (do ((methods (get method 'methods) (cdr methods)))
  584.       ((or (endp methods) (eq (caar methods) type))
  585.        (do ((next (cdr methods) (cdr next))
  586.         (function))
  587.        ((endp next))
  588.      (when (and (subclass-p type (caar next))
  589.             (setq function (getf (cdar next) nil)))
  590.        (return (apply function args)))))))
  591.  
  592. ;; WARNING: Does direct substitution, not a code walk
  593. (defmacro symbol-macrolet (varlist &body body)
  594.   (labels ((subst-list (varlist s-exp)
  595.          (cond ((atom s-exp)
  596.             (or (second (assoc s-exp varlist :test #'eq))
  597.             s-exp))
  598.            ;; Stop at quote and function
  599.            ((member (car s-exp) '(quote function)) s-exp)
  600.            (t (setq s-exp (copy-list s-exp))
  601.               (do ((s s-exp (cdr s))
  602.                (prev nil s))
  603.               ((endp s))
  604.             (rplaca s (subst-list varlist (car s))))
  605.               s-exp))))
  606.     `(progn ,@(subst-list varlist body))))
  607.   
  608. ;; WARNING: Does direct substitution, not a code walk
  609. (defmacro with-slots (slots instance &body body)
  610.   (declare (special $type))
  611.   (unless body
  612.     (error "Not enough parameters to WITH-SLOTS"))
  613.   (when (atom instance)
  614.     (if (and (boundp '$type) $type)
  615.     (setq instance `(the ,$type ,instance))
  616.       (unless (boundp '$type)
  617.     (macro-warn "Warning: WITH-SLOTS doesn't know the instance type, use (the TYPE ~s)" instance))))
  618.   `(symbol-macrolet
  619.      ,(mapcar #'(lambda (slot)
  620.           (if (atom slot)
  621.               `(,slot (slot-value ,instance ',slot))
  622.             `(,(first slot) (slot-value ,instance ',(second slot)))))
  623.           slots)
  624.      ,@body))
  625.  
  626. (defmacro slot-value (object slot-name)
  627.   "Get the SLOT-NAME value from object.
  628.    OBJECT MUST BE OF THE FORM (the CLASS object)"
  629.   (let (class slots)
  630.     (if (and (consp object)
  631.          (eq (car object) 'the)
  632.          (setq class (find-class (second object)))
  633.          (third object)
  634.          (not (cdddr object))
  635.          (constantp slot-name))
  636.     (progn
  637.       (setq slot-name (eval slot-name))
  638.       (setq slots (standard-class-slots class))
  639.       (unless (member slot-name slots)
  640.         (error "~s isn't a slot of ~s" slot-name (class-name class)))
  641.       `(,(intern (concatenate 'string (standard-class-prefix class)
  642.                   (string slot-name))
  643.              (symbol-package (standard-class-allocator class)))
  644.         ,object))
  645.       (progn
  646.     (UNLESS class
  647.       (macro-warn "Warning: (slot-value ~a ~a) is slow - add (the TYPE ~a)"
  648.               object slot-name object))
  649.     `(slow-slot-value ,object ,slot-name)))))
  650.  
  651. (defun slow-slot-value (object slot-name)
  652.   (declare (inline find-class))
  653.   (let* ((class (class-of object))
  654.      (reader (intern (concatenate 'string (standard-class-prefix class)
  655.                       (symbol-name slot-name))
  656.              (symbol-package (class-name class)))))
  657.     (funcall reader object)))
  658.  
  659. (defmacro slot-boundp (object slot-name)
  660.   ;; Fake a slot-boundp macro.
  661.   ;; Assume all nil slots are unbound.
  662.   `(slot-value ,object ,slot-name))
  663.  
  664. (defun make-instance (class-name &rest initialize-keywords-and-values)
  665.   (let* ((class (find-class class-name))
  666.      (allocator (standard-class-allocator class))
  667.      (instance (funcall allocator))
  668.      (initargs (default-initargs class initialize-keywords-and-values)))
  669.     (apply 'initialize-instance instance initargs)
  670.     instance))
  671.  
  672. ;;-----------------------------------------------------------------------------
  673.  
  674. (defmacro undefmethod (name lambda-list &body body &environment env)
  675.   (if (consp name)
  676.       (progn
  677.     (unless (eq (car name) 'setf)
  678.       (error "Invalid method name ~s" name))
  679.     (let ((setfer (intern (concatenate 'string "SET-" (string (second name))))))
  680.       `(progn
  681.          (defsetf ,(second name) (foo) (bar) (declare (ignore foo bar))
  682.         (error "No setf defined for ~s" ',(second name)))
  683.          (undefmethod ,setfer ,lambda-list ,@body))))
  684.     (let (type n args code decs doc
  685.       (qualifier nil)
  686.       $method $type $lambda-list)
  687.       (declare (special $method $type $lambda-list))
  688.       (when (keywordp lambda-list)
  689.     (setq qualifier lambda-list
  690.           lambda-list (pop body)))
  691.       (multiple-value-setq (code decs doc)
  692.     (parse-body body env))
  693.       ;; Find the (first) specialized parameter in the lambda list,
  694.       (do ((arguments lambda-list (cdr arguments))
  695.        (arg)
  696.        (i 0 (1+ i)))
  697.       ((or (endp arguments)
  698.            (member (setq arg (car arguments)) lambda-list-keywords))
  699.        (error "Method has no type"))
  700.     (when (consp arg)
  701.       ;; Save the type and position of the specialized parameter
  702.       (setq type (second arg)
  703.         n i
  704.         args (copy-list lambda-list))
  705.       ;; Special case for EQL types
  706.       (when (and (consp type) (eq (car type) 'eql))
  707.         (unless (constantp (cadr type))
  708.           (error "Non-constant parameter specializers not supported"))
  709.         (setq type (list 'member (eval (cadr type)))))
  710.       ;; Form the method arglist
  711.       (setf (nth i args) (first arg))
  712.       (return nil)))
  713.       ;; Generate code for method and generic method
  714.       (let* ((code-name (intern (format nil "~@[~a-~]~a-~a-METHOD" qualifier name type)
  715.                 (symbol-package name)))
  716.          (methods (delete-method name type code-name qualifier)))
  717.     `(progn
  718.        (eval-when (compile load eval) (setf (get ',name 'methods) ',methods))
  719.        ;; Define the method
  720.        (when (fboundp ',code-name)
  721.          (fmakunbound ',code-name))
  722.        ;; Define the generic method
  723.        ,(if methods
  724.         `(inhibit-fdefine-warnings
  725.            ,(make-generic-function name n methods :documentation doc))
  726.           ;; Remove the generic method also
  727.           `(when (fboundp ',name) (fmakunbound ',name)))
  728.        ',name)))))
  729.